package edu.uow.scsse.main.metrics;

import java.util.List;

import org.apache.log4j.Logger;

import edu.uow.scsse.main.domain.Edge;
import edu.uow.scsse.main.domain.EdgeType;
import edu.uow.scsse.main.domain.Graph;
import edu.uow.scsse.main.domain.Node;
import edu.uow.scsse.main.domain.NodeType;

/**
 * Design Metrics Calculation
 * @author Tiancheng Zhang
 *
 */
public class DesignMetricsCalculation {

	
	private static final Logger logger = Logger.getLogger(DesignMetricsCalculation.class);
	/**Trigging Event Domain Graph List*/
	private List<Graph> trDomainGraphList;
	private double sumNumerator;
	private double sumDenominator;
	
	/**
	 * Constructor
	 * @param trDomainGraphList
	 */
	public DesignMetricsCalculation(List<Graph> trDomainGraphList)
	{
		this.trDomainGraphList = trDomainGraphList;
		this.sumNumerator = 0;
		this.sumDenominator = 0;
	}
	
	/**
	 *Reset for next time usage
	 *@param trDomainGraphList
	 */
	public void resetAllVariables(List<Graph> trDomainGraphList)
	{
		this.trDomainGraphList = trDomainGraphList;
		this.sumNumerator = 0;
		this.sumDenominator = 0;
	}
	
	/**
	 * reactivness Calculation
	 * @return reactiveness design metrics
	 */
	public double reactivnessCalculation()
	{
		double numberOfTriggingEvent = 0;
		double sum = 0;
		
		for(Graph eachGraph: trDomainGraphList)
		{
			sumNumerator = 0;
			sumDenominator = 0;
			if(eachGraph.getNodes().size() == 0)
				continue;
			
			logger.info("Start Calculating for Trigging Event: " + eachGraph.findNodeById(-1).getName());
			DFSAltPlanPriorityCalculation(eachGraph, -1);
			logger.info("End of Calculating for Trigging Event: " + eachGraph.findNodeById(-1).getName() + " With reactiveness:" + ((sumDenominator == 0)? 0 : sumNumerator/sumDenominator));
			
			sum += (sumDenominator == 0)? 0 : sumNumerator/sumDenominator;

			numberOfTriggingEvent++;
		}
		return sum/numberOfTriggingEvent;
	}
	
	/**
	 *Calculate reactiveness for each domain of triggering event based on Alternative plan priority DFS
	 *@param domainGraph
	 *@param currentNode
	 */
	private void DFSAltPlanPriorityCalculation(Graph domainGraph, int currentNodeId)
	{
		logger.info("Visited Nodes : " + numOfVisitedNodes(domainGraph) + " Total Nodes: " + domainGraph.getNodes().size());
		if(numOfVisitedNodes(domainGraph) != domainGraph.getNodes().size())
		{
			Node currentNode = domainGraph.findNodeById(currentNodeId);
			logger.info("Processing Node: " + (currentNode != null? currentNode.getName():"null"));
			//not visited
			if(currentNode != null && !currentNode.getVisitedFlag())
			{
				if(currentNode.getType().equals(NodeType.SUBGOAL))
				{
					sumNumerator++;
				}
				else if(currentNode.getType().equals(NodeType.CONTEXT_CONDITION) && currentNode.getOutgoingEdges().size() == 1 
						&& (domainGraph.findNodeById(currentNode.getOutgoingEdges().get(0).getDestinationId()).getType().equals(NodeType.ACTION)
						||domainGraph.findNodeById(currentNode.getOutgoingEdges().get(0).getDestinationId()).getType().equals(NodeType.SUBGOAL)))
				{
					sumNumerator++;
				}
				else if(currentNode.getType().equals(NodeType.ACTION))
				{
					sumDenominator++;
				}
				//set to visited
				domainGraph.findNodeById(currentNodeId).setVisitedFlag(true);
				logger.info("Set Node: " + (currentNode != null? currentNode.getName():"null") + "->VISITED");
				//alternative plan priority
				if(currentNode.getType().equals(NodeType.SUBGOAL) || currentNode.getType().equals(NodeType.TRIGGINGEVENT))
				{
					for(Edge eachEdge: currentNode.getOutgoingEdges())
					{
						logger.info("Has Outgoing Edge Pointing to " + domainGraph.findNodeById(eachEdge.getDestinationId()).getName());
						//if the subgoal has alternative plan and the node being linked is not visited
						if(eachEdge.getType().equals(EdgeType.ALTPLANEDGE) && !(domainGraph.findNodeById(eachEdge.getDestinationId())).getVisitedFlag())
						{
							DFSAltPlanPriorityCalculation(domainGraph, eachEdge.getDestinationId());
						}
					}
				}
				
				//DFS other nodes
				for(Edge eachEdge: currentNode.getOutgoingEdges())
				{
					logger.info("Has Outgoing Edge Pointing to " + domainGraph.findNodeById(eachEdge.getDestinationId()).getName());
					//if the subgoal has alternative plan and the node being linked is not visited
					if(eachEdge.getType().equals(EdgeType.SEQUENCIALEDGE) && !(domainGraph.findNodeById(eachEdge.getDestinationId())).getVisitedFlag())
					{
						DFSAltPlanPriorityCalculation(domainGraph, eachEdge.getDestinationId());
					}
				}
				
			}
		}
		else
			return;
	}
	
	/**
	 * Number of visited Node
	 * @param domainGraph
	 * @return visitedNode
	 */
	private int numOfVisitedNodes(Graph domainGraph)
	{
		int nodesVisited = 0;
		for(Node eachNode: domainGraph.getNodes())
		{
			if(eachNode.getVisitedFlag() == true)
				nodesVisited++;
		}
		return nodesVisited;
	}
	
	
}
